package com.tianji.learning.utils;

import cn.hutool.core.util.ObjectUtil;
import com.tianji.common.utils.JsonUtils;
import com.tianji.common.utils.StringUtils;
import com.tianji.learning.domain.po.LearningLesson;
import com.tianji.learning.domain.po.LearningRecord;
import com.tianji.learning.mapper.LearningLessonMapper;
import com.tianji.learning.mapper.LearningRecordMapper;
import lombok.Data;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import java.time.Duration;
import java.time.LocalDateTime;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.DelayQueue;

/**
 * @author Administrator
 * @version 1.0
 * @description: TODO
 * @date 2024/6/23 17:02
 */
@Slf4j
@Component
@RequiredArgsConstructor
public class LearningRecordDelayTaskHandler {
    private final StringRedisTemplate redisTemplate;
    private final static String KEY_TEMPLATE = "learning:record:{}";
    private DelayQueue<DelayTask<RecordTaskData>> delayTaskQueue = new DelayQueue<>();
    private final LearningRecordMapper recordMapper;
    private final LearningLessonMapper lessonMapper;
    private static Boolean flag = true;

    @PostConstruct
    public void init(){
        log.info("Bean初始化阶段，开启一个新的线程执行延时检测任务...");
        // new Thread(() -> handlerDelayTask()).start();
        CompletableFuture.runAsync(() -> handlerDelayTask());
    }

    @PreDestroy
    public void destory(){
        log.info("Bean销毁...");
        flag = false;
    }

    public void handlerDelayTask(){
        log.info("开始处理播放进度延时检测任务逻辑...");
        while (flag){
            try {
                // 1、从延时队列中尝试take获取延时检测任务
                DelayTask<RecordTaskData> delayTask = delayTaskQueue.take();
                RecordTaskData data = delayTask.getData();
                log.info("获取到了延时检测任务数据：{}",data);

                // 2、从redis中查询实时的播放进度
                LearningRecord redisRecord = readLearningRecordFromCache(data.getLessonId(), data.getSectionId());

                // 3、比较延时任务中的本地进度  与 redis中的实时进度是否一致
                // 3.1、如果不一致，无需处理，直接跳过
                if(ObjectUtil.isEmpty(redisRecord) || ObjectUtil.notEqual(redisRecord.getMoment(), data.getMoment())){
                    continue;
                }

                // 3.2、如果一致，证明用户已经离开了，需要将redis中的进度更新到学习记录表,并且更新课表数据
                // 3.2.1、更新学习记录表（主要是更新播放进度）
                redisRecord.setFinished(null);
                recordMapper.updateById(redisRecord);

                // 3.2.2、更新课表数据（更新最近学习小节、最近学习的时间）
                LearningLesson lesson = new LearningLesson();
                lesson.setId(data.getLessonId());
                lesson.setLatestSectionId(data.getSectionId());
                lesson.setLatestLearnTime(LocalDateTime.now().minusSeconds(20));
                lessonMapper.updateById(lesson);
            } catch (Exception e) {
                log.error("执行播放进度延时检测任务失败！！！原因：{}", e);
            }
        }
    }

    /**
     * 1、从缓存中读取学习记录
     * @param lessonId 课表ID
     * @param sectionId 小节ID
     * @return
     */
    public LearningRecord readLearningRecordFromCache(Long lessonId, Long sectionId) {
        log.info("从缓存中查询学习记录，课表ID：{}，小节ID：{}",lessonId,sectionId);
        // 1、组装key
        String key = StringUtils.format(KEY_TEMPLATE, lessonId);

        // 2、组装hashkey
        String hashKey = sectionId.toString();

        // 3、发起查询，得到hashvalue
        String hashValue = (String)redisTemplate.opsForHash().get(key, hashKey);
        if(ObjectUtil.isEmpty(hashValue)){
            log.warn("缓存中没有学习记录，课表ID：{}，小节ID：{}",lessonId,sectionId);
            return null;
        }

        // 4、将hashvalue转化成LearningRecord
        LearningRecord learningRecord = JsonUtils.toBean(hashValue, LearningRecord.class);

        // 5、返回数据
        return learningRecord;
    }

    /**
     * 2、将学习记录写入缓存
     * @param learningRecord
     */
    public void writeLearningRecordToCache(LearningRecord learningRecord) {
        log.info("向缓存中写入学习记录，数据：{}",learningRecord);
        // 1、组装key
        String key = StringUtils.format(KEY_TEMPLATE, learningRecord.getLessonId());

        // 2、组装hashkey
        String hashKey = learningRecord.getSectionId().toString();

        // 3、得到hashValue
        String hashValue = JsonUtils.toJsonStr(new RedisRecordData(learningRecord));

        // 4、写入缓存，并设置过期时间为1分钟
        redisTemplate.opsForHash().put(key,hashKey,hashValue);
        redisTemplate.expire(key, Duration.ofMinutes(1));
    }

    /**
     * 3、添加学习记录延时检测任务
     * @param learningRecord
     */
    public void addLearningRecordDelayCheckTask(LearningRecord learningRecord) {
        log.info("提交学习记录延时检测任务，学习记录：{}",learningRecord);
        // 1、将学习记录缓存到Redis
        writeLearningRecordToCache(learningRecord);
        // 2、提交延时检测任务
        delayTaskQueue.add(new DelayTask<>(Duration.ofSeconds(20),new RecordTaskData(learningRecord)));
    }

    /**
     * 4、清除学习记录缓存
     * @param lessonId
     * @param sectionId
     */
    public void cleanLearningRecordCache(Long lessonId, Long sectionId) {
        log.info("清除学习记录缓存，课表ID：{}，小节ID：{}",lessonId,sectionId);
        // 1、组装key
        String key = StringUtils.format(KEY_TEMPLATE, lessonId);

        // 2、组装hashkey
        String hashKey = sectionId.toString();

        // 3、删除缓存
        this.redisTemplate.opsForHash().delete(key,hashKey);
    }

    @Data
    class RedisRecordData{
        private Long id;
        private Integer moment;
        private Boolean finished;

        public RedisRecordData(LearningRecord learningRecord) {
            this.id = learningRecord.getId();
            this.moment = learningRecord.getMoment();
            this.finished = learningRecord.getFinished();
        }
    }

    @Data
    class RecordTaskData{
        private Long lessonId;
        private Long sectionId;
        private Integer moment;

        public RecordTaskData(LearningRecord learningRecord) {
            this.lessonId = learningRecord.getLessonId();
            this.sectionId = learningRecord.getSectionId();
            this.moment = learningRecord.getMoment();
        }
    }
}
